Skip to content

Keep the docs deploy on pnpm so npm never runs - #2

Merged
karngyan merged 1 commit into
mainfrom
fix/docs-deploy-pnpm-wrangler
Aug 9, 2026
Merged

Keep the docs deploy on pnpm so npm never runs#2
karngyan merged 1 commit into
mainfrom
fix/docs-deploy-pnpm-wrangler

Conversation

@karngyan

@karngyan karngyan commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

deploy-docs failed on the merge of #1. The failure is in cloudflare/wrangler-action@v3, not in wrangler (run 31318765703):

[command] npx --no-install wrangler --version
npm error npx canceled due to missing packages and no YES option: ["wrangler@4.120.0"]
⚠️ Wrangler not found or version is incompatible. Installing...
[command] npm i wrangler@4
npm error code EUNSUPPORTEDPROTOCOL
npm error Unsupported URL Type "workspace:": workspace:*

Why

The action sniffs for a package manager by looking for a lockfile, but it looks in workingDirectory, not the repo root:

function detectPackageManager(workingDirectory = ".") {
  if (existsSync(join(workingDirectory, "package-lock.json"))) return "npm";
  if (existsSync(join(workingDirectory, "yarn.lock"))) return "yarn";
  if (existsSync(join(workingDirectory, "pnpm-lock.yaml"))) return "pnpm";
  ...
  return null;
}
// PACKAGE_MANAGERS[name || detectPackageManager(workingDirectory) || "npm"]

apps/docs has no lockfile of its own (ours is at the repo root), so detection returns null and it falls back to npm. It probes with npx --no-install wrangler --version, finds nothing because wrangler was not a dependency of anything, then runs npm i wrangler@4 inside apps/docs, where npm hits "dowel": "workspace:*" and refuses.

karnstack/flue does not hit this only because its site/ has no workspace dependency for npm to choke on. It is still silently running npm inside a pnpm repo.

Fix

Two changes. Either one is sufficient on its own; together npm is never reached even if one regresses.

  1. packageManager: pnpm on the action step. A documented v3 input, and the supported override for the lockfile sniff. It switches the probe to pnpm exec wrangler --version, the run to pnpm exec wrangler deploy, and the fallback install to pnpm add, which understands workspace:.
  2. wrangler ^4.120.0 as a devDependency of apps/docs. pnpm install --frozen-lockfile already runs before the deploy step, so the probe now finds a real wrangler, 4.120.0 satisfies the pinned "4", and the action skips its install step entirely. It also pins the deploy tool in the lockfile rather than floating on whatever npm resolves that morning, and makes a local pnpm exec wrangler deploy --dry-run reproduce CI exactly.

workerd joins allowBuilds in pnpm-workspace.yaml: it arrives as a wrangler dependency, its install script links the platform binary, and pnpm 11 exits 1 on an install that silently skipped a build script.

Verification

The load-bearing check: the action's own bundle (cloudflare/wrangler-action@v3 dist/index.mjs) run against this checkout with the same inputs as the workflow and a bogus API token.

::group::🔍 Checking for existing Wrangler installation
[command] pnpm exec wrangler --version
4.120.0
✅ Using Wrangler 4.120.0
::endgroup::
::group::🚀 Running Wrangler Commands
[command] pnpm exec wrangler deploy
 ⛅️ wrangler 4.120.0
✘ [ERROR] A request to the Cloudflare API (/accounts) failed.
  Invalid format for Authorization header [code: 6111]

No install step, no npm process, and it reaches the Cloudflare API before failing on the fake credential. That is the whole action path end to end.

Also green:

  • npx --no-install wrangler --version from apps/docs now prints 4.120.0 too, so the fix holds even without the packageManager input.
  • pnpm install --frozen-lockfile succeeds. The lockfile change is wrangler plus esbuild entering vite's peer resolution set.
  • pnpm exec wrangler deploy --dry-run in apps/docs reads all 46 files from dist/.
  • pnpm --filter dowel build && pnpm --filter @dowel/docs build, 11 pages prerendered.
  • Root gate in CI order: pnpm format:check, pnpm typecheck, pnpm build, pnpm test (81 tests, 10 files).

What stays unproven

The real deploy. It needs the org CLOUDFLARE_API_TOKEN, which is not available to a PR, so the only untested segment is what happens after authentication succeeds: asset upload and the dowel.sh custom-domain route binding. Everything up to and including the API call is verified above. This will not be known good until it merges and deploy-docs runs on main.

No repo-level secret was created, so the org secret keeps its visibility.

Unchanged

The job still skips rather than fails when the token is absent, dowel still builds before @dowel/docs, the toolchain still comes from mise.toml via jdx/mise-action@v4, and apps/docs/wrangler.jsonc is untouched.

🤖 Generated with Claude Code

The deploy-docs run on the merge to main died in wrangler-action, not in
wrangler:

  [command] npx --no-install wrangler --version
  npm error npx canceled due to missing packages
  [command] npm i wrangler@4
  npm error code EUNSUPPORTEDPROTOCOL
  npm error Unsupported URL Type "workspace:": workspace:*

The action picks a package manager by looking for a lockfile, but it looks
in workingDirectory, not the repo root. apps/docs has no lockfile of its
own, so detection returns null and the action falls back to npm. It then
probes with `npx --no-install wrangler --version`, finds nothing (wrangler
was not a dependency of anything), and shells out to `npm i wrangler@4`
inside apps/docs, where npm meets "dowel": "workspace:*" and gives up.

Two changes, either of which fixes it on its own. Together they mean npm is
never reached even if one regresses.

1. packageManager: pnpm on the action. The input exists in v3 and is the
   supported way to override the lockfile sniff. It switches the probe to
   `pnpm exec wrangler --version`, the run command to `pnpm exec wrangler
   deploy`, and the fallback install to `pnpm add`, which understands the
   workspace protocol.

2. wrangler ^4.120.0 as a devDependency of apps/docs. pnpm install already
   runs before the deploy step, so the probe now finds a real wrangler,
   the version satisfies the pinned "4", and the action skips its install
   step entirely. This also pins the deploy tool in the lockfile instead of
   floating on whatever npm resolved that morning, and makes a local
   `pnpm exec wrangler deploy --dry-run` match CI.

workerd joins allowBuilds because it arrives with wrangler and pnpm 11
exits 1 on an install that silently skipped a build script. Its install
script links the platform binary.

Verified by running the action's own bundle (cloudflare/wrangler-action@v3
dist/index.mjs) against this checkout with a bogus API token: the probe
reports "Using Wrangler 4.120.0", no install step runs, no npm process is
spawned, and the run reaches the Cloudflare API before failing on the fake
credential. Separately, `pnpm exec wrangler deploy --dry-run` in apps/docs
reads all 46 files from dist/. The real deploy stays unproven until this
merges, since it needs the org token.

Behaviour is otherwise unchanged: the job still skips rather than fails
without CLOUDFLARE_API_TOKEN, dowel still builds before @dowel/docs, mise
still supplies the toolchain, and wrangler.jsonc is untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@karngyan
karngyan merged commit 77286e3 into main Aug 9, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant